<template>
  <DefineTemplate
    v-slot="{
      field: { name, label, type, options, inputAttributes, color, size },
    }"
  >
    <label class="label" v-if="label">
      <span>{{ label }}</span>
    </label>
    <Field :name="name" #="{ field, handleChange }">
      <slot :name="name" :field="field" :handleChange="handleChange">
        <ZCalendar
          v-if="type == 'calendar'"
          :model-value="field.value"
          @update:model-value="handleChange"
        />
        <ZCalendarDropdown
          v-else-if="type == 'calendar-dropdown'"
          :model-value="field.value"
          @update:model-value="handleChange"
          v-bind="inputAttributes"
        />
        <div v-else-if="type == 'checkbox'" class="flex gap-2 items-center">
          <label class="label" v-for="option of options" :key="option.value">
            <ZCheckbox
              :value="option.value"
              :model-value="field.value"
              @update:model-value="handleChange"
            />
            <span>{{ option.label }}</span>
          </label>
        </div>
        <div v-else-if="type == 'radio'" class="flex gap-2 items-center">
          <label class="label" v-for="option of options" :key="option.value">
            <ZRadio
              :name="name"
              :value="option.value"
              :model-value="field.value"
              @update:model-value="handleChange"
            />
            <span>{{ option.label }}</span>
          </label>
        </div>
        <ZSelect
          v-else-if="type == 'select'"
          class="select w-full"
          :model-value="field.value"
          @update:model-value="handleChange"
          :options="options!"
          v-bind="{ ...inputAttributes, color, size }"
        />
        <ZTextarea
          v-else-if="type == 'textarea'"
          :model-value="field.value"
          @update:model-value="handleChange"
          v-bind="{ ...inputAttributes, color, size }"
        />
        <Suspense v-else-if="type == 'markdown'">
          <ZMarkdownEditor
            :model-value="field.value"
            @update:model-value="handleChange"
          />
        </Suspense>

        <ZFileInput
          v-else-if="type == 'file'"
          type="file"
          class="file-input file-input-primary w-full"
          :model-value="field.value"
          @update:model-value="handleChange"
          v-bind="{ ...inputAttributes, color, size }"
        />
        <ZInput
          v-else
          class="input w-full"
          :type="type || 'text'"
          :model-value="field.value"
          @update:model-value="handleChange"
          v-bind="{ ...inputAttributes, color, size }"
        />
      </slot>
    </Field>
    <ErrorMessage :name="name" class="label-text-alt text-error"></ErrorMessage>
  </DefineTemplate>

  <Form
    name="z-form"
    ref="form"
    :validation-schema="toTypedSchema(schema)"
    :initial-values="initialValues"
    @submit="(values, actions) => onSubmit?.(values as V, actions)"
  >
    <fieldset class="fieldset">
      <template
        v-for="field of fields"
        :key="
          Array.isArray(field) ? field.map((f) => f.name).join('-') : field.name
        "
      >
        <div class="flex gap-2" v-if="Array.isArray(field)">
          <div
            class="flex-1 flex flex-col gap-2"
            v-for="f of field"
            :key="f.name"
          >
            <ReuseTemplate :field="f" />
          </div>
        </div>
        <div class="flex flex-col gap-2" v-else>
          <ReuseTemplate :field="field" />
        </div>
      </template>
      <slot></slot>
    </fieldset>
  </Form>
</template>
<script setup lang="ts" generic="T extends z.ZodSchema, V = z.infer<T>">
import { useTemplateRef, watch } from "vue";
import { createReusableTemplate } from "@vueuse/core";
import {
  Form,
  Field,
  ErrorMessage,
  type SubmissionContext,
} from "vee-validate";
import { toTypedSchema } from "@vee-validate/zod";
import * as z from "zod";
import type { FormField, FormFields } from "./types";
import ZCheckbox from "./ZCheckbox.vue";
import ZRadio from "./ZRadio.vue";
import ZCalendar from "./ZCalendar.vue";
import ZCalendarDropdown from "./ZCalendarDropdown.vue";
import ZInput from "./ZInput.vue";
import ZFileInput from "./ZFileInput.vue";
import ZSelect from "./ZSelect.vue";
import ZTextarea from "./ZTextarea.vue";
import ZMarkdownEditor from "./ZMarkdownEditor.vue";

interface Props {
  schema: T;
  onSubmit?: (values: V, actions: SubmissionContext) => void;
  fields: FormFields;
  initialValues?: Partial<V>;
}

const props = defineProps<Props>();

const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
  field: FormField;
}>();

const form = useTemplateRef<InstanceType<typeof Form>>("form");
watch(
  () => props.initialValues,
  (value) => {
    if (value) {
      form.value?.resetForm({ values: value });
      form.value?.setValues(value);
    }
  },
  {
    immediate: true,
  }
);
</script>
